package edu.stanford.bmir.protege.web.server.stkosutil.enricher;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;


import java.util.Set;

import org.semanticweb.owlapi.model.OWLClass;
import org.semanticweb.owlapi.model.OWLDataProperty;
import org.semanticweb.owlapi.model.OWLEntity;
import org.semanticweb.owlapi.model.OWLObjectProperty;

import edu.stanford.bmir.protege.web.client.rpc.data.Triple;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.common.TripleData;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.common.UtilConstants;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.enricher.CandidateEntity;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.enricher.EnricherSource;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.enricher.EnrichmentOptions;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.enricher.EnrichmentResult;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.enricher.OriginEntity;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.enricher.SourceThesaurus;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.statistics.StatisticsResult;
import edu.stanford.bmir.protege.web.server.owlapi.AnnotationsTreatment;
import edu.stanford.bmir.protege.web.server.owlapi.NonAnnotationTreatment;
import edu.stanford.bmir.protege.web.server.owlapi.OWLAPIProject;
import edu.stanford.bmir.protege.web.server.owlapi.OWLAPIProjectManager;
import edu.stanford.bmir.protege.web.server.owlapi.RenderingManager;
import edu.stanford.bmir.protege.web.server.owlapi.TripleMapper;
import edu.stanford.bmir.protege.web.server.owlapi.TripleMapperSelector;
import edu.stanford.bmir.protege.web.server.stkosutil.common.AppException;
import edu.stanford.bmir.protege.web.server.stkosutil.statistics.EntityCounter;
import edu.stanford.bmir.protege.web.shared.project.ProjectId;
import edu.stanford.bmir.protege.web.shared.user.UserId;

public class OntologyEnricher {
	private OWLAPIProject getProject(String projectName) {
        if (projectName == null) {
            throw new NullPointerException("projectName must not be null");
        }
        ProjectId projectId = ProjectId.get(projectName);
        // TODO: Log
        return getProject(projectId);
    }

    /**
     * Gets the OWLAPIProject for a given {@link ProjectId}.If a project with the specified id exists then that project
     * will be returned, otherwise, a fresh project will be created and that fresh project returned.
     * @param projectId The id of the project.
     * @return The OWL API project. Not <code>null</code>.
     */
    private OWLAPIProject getProject(ProjectId projectId) {
        OWLAPIProjectManager pm = OWLAPIProjectManager.getProjectManager();
        return pm.getProject(projectId);
    }

	public List<CandidateEntity> fetchCandidateEntities(String projectName,
			OriginEntity originEntity, EnrichmentOptions options) {
		List<CandidateEntity> result = null;
		ClassEnricher classEnricher = new ClassEnricher();
		result = classEnricher.fetchCandidateEntities(projectName, originEntity, options);
		return result;
	}
	 	    
	public List<TripleData> fetchCandidateTripleList(String projectName, OriginEntity originEntity, EnrichmentOptions options) throws AppException {
		OWLAPIProject project = this.getProject(projectName); 
		Set<String> sClassBrowserTexts = new HashSet<String>();
		Set<String> sDPBrowserTexts = new HashSet<String>();
		Set<String> sOPBrowserTexts = new HashSet<String>();
		RenderingManager rm = project.getRenderingManager();
		
		Set<OWLClass> sClassList = project.getRootOntology().getClassesInSignature();
		for (OWLClass cls: sClassList){
			  String browserText = rm.getBrowserText(cls);
			  sClassBrowserTexts.add(browserText.toLowerCase());
		}
		
		Set<OWLDataProperty> sDataPropertyList = project.getRootOntology().getDataPropertiesInSignature();
		for (OWLDataProperty dataProperty: sDataPropertyList){
			String browserText = rm.getBrowserText(dataProperty);
			sDPBrowserTexts.add(browserText.toLowerCase());
		}
		
		Set<OWLObjectProperty> sObjectPropertyList = project.getRootOntology().getObjectPropertiesInSignature();
		for (OWLObjectProperty objectProperty: sObjectPropertyList){
			String browserText = rm.getBrowserText(objectProperty);
			sOPBrowserTexts.add(browserText.toLowerCase());
		}
		
		
		String subject = originEntity.getBrowserText();
		try{
			List<TripleData> result  = KnowlegeBaseClient.getInstance().fetchCandidateTripleList(subject, options);
			if (result == null){
				return new ArrayList<TripleData>();
			}
			List<Triple> currentTriples = this.getEntityTriples(projectName, originEntity.getName());
			result = this.extractFreshTriples(currentTriples, result);
			for (int i=result.size()-1; i>=0; i--){
				TripleData triple = result.get(i);
				String predicate = triple.getPredicate().toLowerCase();
				if (predicate.compareTo(UtilConstants.REL_HAS_SUBCLASS)==0){
					triple.setPredicateIsNew(false);
				}else{
					if (sDPBrowserTexts.contains(predicate)){
						 result.remove(i);
					}
					if (sOPBrowserTexts.contains(predicate)){
						triple.setPredicateIsNew(false);
					}else{
						triple.setPredicateIsNew(true);
					}
				}
				if (sClassBrowserTexts.contains(triple.getObject().toLowerCase())){
					triple.setObjectIsNew(false);
				}else{
					triple.setObjectIsNew(true);
				}
			}
			return result;
		}catch(Exception ex){
			ex.printStackTrace();
			return new ArrayList<TripleData>();
		}
	}
	 private List<TripleData> extractFreshTriples(List<Triple> currentTriples, List<TripleData> candidateTriples){
		  List<TripleData> alFreshTripleData = new ArrayList<TripleData>();
		  for (TripleData candidateTriple: candidateTriples){
			  boolean bExists = false;
			  for (Triple currentTriple: currentTriples){
				  if (currentTriple.getProperty().getBrowserText().equalsIgnoreCase(candidateTriple.getPredicate())
					&& currentTriple.getValue().getBrowserText().equalsIgnoreCase(candidateTriple.getObject())
						  ){
					  bExists = true;
				  }
			  }
			  if (bExists==false){
				  alFreshTripleData.add(candidateTriple);
			  }
		  }
		  return alFreshTripleData;
	  }
	 
	 private List<Triple> getEntityTriples(final String projectName, String entityName) {
	        final Set<Triple> result = new LinkedHashSet<Triple>();
	        TripleMapperSelector selector = new TripleMapperSelector(getProject(projectName), AnnotationsTreatment.EXCLUDE_ANNOTATIONS, NonAnnotationTreatment.INCLUDE_NON_ANNOTATIONS);
	        OWLAPIProject project = getProject(projectName);
	        Set<OWLEntity> entities = project.getRenderingManager().getEntities(entityName);
	        for (OWLEntity entity : entities) {
	            TripleMapper<?> mapper = selector.getMapper(entity);
	            if (mapper != null) {
	                result.addAll(mapper.getTriples());
	            }
	        }
	        return new ArrayList<Triple>(result);
	  }
	 
	public List<SourceThesaurus> fetchSourceThesaurusList(){
		try{
			return KnowlegeBaseClient.getInstance().fetchSourceThesaurusList();
		}catch(Exception ex){
			ex.printStackTrace();
			return new ArrayList<SourceThesaurus>();
		}
	}
	public EnrichmentResult enrichOntology(String projectName,
			EnrichmentOptions options,UserId userId) {
		EnrichmentResult result  = null;
		if (options.getSource()==EnricherSource.DOC_TRIPLE){
			PropertyEnricher propertyEnricher = new PropertyEnricher();
			try{
				result = propertyEnricher.enrichOntolgoy(projectName, userId);
			}catch(Exception ex){
				ex.printStackTrace();
				return null;
			}
		}else{
			try{
				ClassEnricher classEnricher = new ClassEnricher();
				result = classEnricher.enrichOntology(projectName, options, userId);
			}catch(Exception ex){
				ex.printStackTrace();
				return null;
			}
		}
		EntityCounter entityCounter = new EntityCounter();
		StatisticsResult result2 = entityCounter.countEntities(projectName);
		result.setEnrichedClassNumber(result2.getEnrichedClassNumber());
		result.setEnrichedPropertyNumber(result2.getEnrichedPropertyNumber());
		result.setOriginalClassNumber(result2.getOriginalClassNumber());
		result.setOriginalPropertyNumber(result2.getOriginalPropertyNumber());
		result.setTotalClassNumber(result2.getTotalClassNumber());
		result.setTotalPropertyNumber(result2.getTotalPropertyNumber());
		return result;
	}
	
}
